OK, in your example this would be done by having two different contents, where each content has a data field with the same value (10159 in your example). When you visit a URL that asks for a content with data field value 10159 in the context of the language “en”, your controller would then do a query for content where e.g. myCustomArticleID=“10159” AND language=“en”
While the above method works, it isn’t necessarily the most elegant way of implementing multi-language content, unless you for instance need to integrate your data with other systems that do scripted requests that rely on the same 10159 ID, or if your requirement is that the URL remains short and that users are supposed to be able to replace “vi” with “en” in the address bar to view the contents in another language. On your example site, clicking one of the language flags in the header should ideally take you to the content in that language, but I see that it only takes you to the language root of the site, e.g. /en if you click the English flag. We can do it better than that!
A more elegant way of implementing your example would be to have the content in the default language being shown without the ID: https://o7planning.org/vi/bat-dau-voi-java-can-nhung-gi and instead automatically resolve a link to the “en” version here https://o7planning.org/en/what-is-needed-to-get-started-with-java that you use as link on the language flags in the header. The link would be resolved by doing a query for a common value in the back-end (for instance the 10159 value, related content, or using the new awesome Layers feature that is coming soon in Enonic XP where you basically just ask for the same content in a different branch).
In other words, your example follows a pattern that is more technical and less user-friendly than it needs to be in Enonic XP, where we instead just do queries in the back-end code that resolves the correct content based on the context language and then delivers that content to the user.