diff --git a/CHANGELOG.md b/CHANGELOG.md index aef8bd7054..a76f5cd71f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Doubled range of ScaleMode slider when set to Custom - Glider can now be deployed even when not on the ground - Gliding now has an energy cost for strenuous maneuvers based on lift +- Translations are now folders with multiple files instead of a huge single file ### Removed diff --git a/assets/voxygen/i18n/PL.ron b/assets/voxygen/i18n/PL/_manifest.ron similarity index 99% rename from assets/voxygen/i18n/PL.ron rename to assets/voxygen/i18n/PL/_manifest.ron index 545eae7ce1..415e236d91 100644 --- a/assets/voxygen/i18n/PL.ron +++ b/assets/voxygen/i18n/PL/_manifest.ron @@ -27,6 +27,7 @@ scale_ratio: 1.0, ), }, + sub_directories: [], string_map: { /// Start Common section // Texts used in multiple locations with the same formatting diff --git a/assets/voxygen/i18n/README.md b/assets/voxygen/i18n/README.md new file mode 100644 index 0000000000..3530f6654f --- /dev/null +++ b/assets/voxygen/i18n/README.md @@ -0,0 +1,17 @@ +# Translation document instructions + +In order to keep localization documents readible please follow the following +rules: +- Separate the string map sections using a commentary describing the purpose + of the next section +- Prepend multi-line strings with a commentary +- Append one blank line after multi-line strings and two blank lines after sections + + +# Adding a new language in Veloren + +To add a new language in Veloren, please follow these steps: +- Create a new folder into the `assets/voxygen/i18n` directory +- Copy the content of the `en` directory in your new folder +- Configure the language metadata in the `_manifest.ron` file +- From this point, you can start translating the files! diff --git a/assets/voxygen/i18n/de_DE.ron b/assets/voxygen/i18n/de_DE/_manifest.ron similarity index 99% rename from assets/voxygen/i18n/de_DE.ron rename to assets/voxygen/i18n/de_DE/_manifest.ron index 79a82a388f..02f81f3753 100644 --- a/assets/voxygen/i18n/de_DE.ron +++ b/assets/voxygen/i18n/de_DE/_manifest.ron @@ -39,6 +39,7 @@ scale_ratio: 1.0, ), }, + sub_directories: [], string_map: { /// Start Common section /// Texts used in multiple locations with the same formatting diff --git a/assets/voxygen/i18n/en.ron b/assets/voxygen/i18n/en.ron deleted file mode 100644 index 52aebd2bb0..0000000000 --- a/assets/voxygen/i18n/en.ron +++ /dev/null @@ -1,664 +0,0 @@ -/// Translation document instructions -/// -/// In order to keep localization documents readible please follow the following -/// rules: -/// - separate the string map sections using a commentary describing the purpose -/// of the next section -/// - prepend multi-line strings with a commentary -/// - append one blank lines after a multi-line strings and two after sections -/// -/// To add a new language in Veloren, just write an additional `.ron` file in -/// `assets/voxygen/i18n` and that's it! -/// -/// WARNING: Localization files shall be saved in UTF-8 format without BOM - -/// Localization for "global" English -( - metadata: ( - language_name: "English", - language_identifier: "en", - ), - convert_utf8_to_ascii: false, - fonts: { - "opensans": Font ( - asset_key: "voxygen.font.OpenSans-Regular", - scale_ratio: 1.0, - ), - "metamorph": Font ( - asset_key: "voxygen.font.Metamorphous-Regular", - scale_ratio: 1.0, - ), - "alkhemi": Font ( - asset_key: "voxygen.font.Alkhemikal", - scale_ratio: 1.0, - ), - "wizard": Font ( - asset_key: "voxygen.font.wizard", - scale_ratio: 1.0, - ), - "cyri": Font ( - asset_key: "voxygen.font.haxrcorp_4089_cyrillic_altgr_extended", - scale_ratio: 1.0, - ), - }, - string_map: { - /// Start Common section - // Texts used in multiple locations with the same formatting - "common.username": "username", - "common.singleplayer": "Singleplayer", - "common.multiplayer": "Multiplayer", - "common.servers": "Servers", - "common.quit": "Quit", - "common.settings": "Settings", - "common.languages": "Languages", - "common.interface": "Interface", - "common.gameplay": "Gameplay", - "common.controls": "Controls", - "common.video": "Graphics", - "common.sound": "Sound", - "common.resume": "Resume", - "common.characters": "Characters", - "common.close": "Close", - "common.yes": "Yes", - "common.no": "No", - "common.back": "Back", - "common.create": "Create", - "common.okay": "Okay", - "common.add": "Add", - "common.accept": "Accept", - "common.decline": "Decline", - "common.disclaimer": "Disclaimer", - "common.cancel": "Cancel", - "common.none": "None", - "common.error": "Error", - "common.fatal_error": "Fatal Error", - "common.you": "You", - "common.automatic": "Auto", - "common.random": "Random", - // Settings Window title - "common.interface_settings": "Interface Settings", - "common.gameplay_settings": "Gameplay Settings", - "common.controls_settings": "Controls Settings", - "common.video_settings": "Graphics Settings", - "common.sound_settings": "Sound Settings", - "common.language_settings": "Language Settings", - - // Message when connection to the server is lost - "common.connection_lost": r#"Connection lost! -Did the server restart? -Is the client up to date?"#, - - - "common.species.orc": "Orc", - "common.species.human": "Human", - "common.species.dwarf": "Dwarf", - "common.species.elf": "Elf", - "common.species.undead": "Undead", - "common.species.danari": "Danari", - - "common.weapons.axe": "Axe", - "common.weapons.sword": "Sword", - "common.weapons.staff": "Staff", - "common.weapons.bow": "Bow", - "common.weapons.hammer": "Hammer", - "common.weapons.sceptre": "Healing Sceptre", - "common.rand_appearance": "Random appearance and name", - /// End Common section - - - /// Start Main screen section - "main.username": "Username", - "main.server": "Server", - "main.password": "Password", - "main.connecting": "Connecting", - "main.creating_world": "Creating world", - "main.tip": "Tip:", - - // Welcome notice that appears the first time Veloren is started - "main.notice": r#"Welcome to the alpha version of Veloren! - -Before you dive into the fun, please keep a few things in mind: - -- This is a very early alpha. Expect bugs, extremely unfinished gameplay, unpolished mechanics, and missing features. - -- If you have constructive feedback or bug reports, you can contact us via Reddit, GitLab, or our community Discord server. - -- Veloren is licensed under the GPL 3 open-source licence. That means you're free to play, modify, and redistribute the game however - you wish (provided derived work is also under GPL 3). - -- Veloren is a non-profit community project, and everybody working on it is a volunteer. -If you like what you see, you're welcome to join the development or art teams! - -Thanks for taking the time to read this notice, we hope you enjoy the game! - -~ The Veloren Devs"#, - - // Login process description - "main.login_process": r#"Information on the Login Process: - -Please note that you now need an account -to play on auth-enabled servers. - -You can create an account over at - -https://veloren.net/account/."#, - "main.login.server_not_found": "Server not found", - "main.login.authentication_error": "Auth error on server", - "main.login.server_full": "Server is full", - "main.login.untrusted_auth_server": "Auth server not trusted", - "main.login.outdated_client_or_server": "ServerWentMad: Probably versions are incompatible, check for updates.", - "main.login.timeout": "Timeout: Server did not respond in time. (Overloaded or network issues).", - "main.login.server_shut_down": "Server shut down", - "main.login.already_logged_in": "You are already logged into the server.", - "main.login.network_error": "Network error", - "main.login.failed_sending_request": "Request to Auth server failed", - "main.login.invalid_character": "The selected character is invalid", - "main.login.client_crashed": "Client crashed", - "main.login.not_on_whitelist": "You need a Whitelist entry by an Admin to join", - "main.login.banned": "You have been banned with the following reason", - "main.login.kicked": "You have been kicked with the following reason", - "main.login.select_language": "Select a language", - - "main.servers.select_server": "Select a server", - - /// End Main screen section - - - /// Start HUD Section - "hud.do_not_show_on_startup": "Don't show this on Startup", - "hud.show_tips": "Show Tips", - "hud.quests": "Quests", - "hud.you_died": "You Died", - "hud.waypoint_saved": "Waypoint Saved", - - "hud.press_key_to_show_keybindings_fmt": "[{key}] Keybindings", - "hud.press_key_to_toggle_lantern_fmt": "[{key}] Lantern", - "hud.press_key_to_show_debug_info_fmt": "Press {key} to show debug info", - "hud.press_key_to_toggle_keybindings_fmt": "Press {key} to toggle keybindings", - "hud.press_key_to_toggle_debug_info_fmt": "Press {key} to toggle debug info", - - // Chat outputs - "hud.chat.online_msg": "[{name}] is online now", - "hud.chat.offline_msg": "{name} went offline", - - "hud.chat.default_death_msg": "[{name}] died", - "hud.chat.environmental_kill_msg": "[{name}] died in {environment}", - "hud.chat.fall_kill_msg": "[{name}] died from fall damage", - "hud.chat.suicide_msg": "[{name}] died from self-inflicted wounds", - - "hud.chat.pvp_melee_kill_msg": "[{attacker}] defeated [{victim}]", - "hud.chat.pvp_ranged_kill_msg": "[{attacker}] shot [{victim}]", - "hud.chat.pvp_explosion_kill_msg": "[{attacker}] blew up [{victim}]", - "hud.chat.pvp_energy_kill_msg": "[{attacker}] killed [{victim}] with magic", - "hud.chat.pvp_buff_kill_msg": "[{attacker}] killed [{victim}]", - - - "hud.chat.npc_melee_kill_msg": "{attacker} killed [{victim}]", - "hud.chat.npc_ranged_kill_msg": "{attacker} shot [{victim}]", - "hud.chat.npc_explosion_kill_msg": "{attacker} blew up [{victim}]", - "hud.chat.npc_energy_kill_msg": "[{attacker}] killed [{victim}] with magic", - "hud.chat.npc_other_kill_msg": "[{attacker}] killed [{victim}]", - - "hud.chat.loot_msg": "You picked up [{item}]", - "hud.chat.loot_fail": "Your Inventory is full!", - "hud.chat.goodbye": "Goodbye!", - "hud.chat.connection_lost": "Connection lost. Kicking in {time} seconds.", - - // SCT outputs - "hud.sct.experience": "{amount} Exp", - "hud.sct.block": "BLOCKED", - - // Respawn message - "hud.press_key_to_respawn": r#"Press {key} to respawn at the last campfire you visited."#, - - // Welcome message - "hud.welcome": r#"Welcome to the Veloren Alpha! - - -Some tips before you start: - - -Press F1 to see the available key commands. - -Type /help into the chat to see chat commands - - -There are chests and other objects randomly spawning in the World! - -Right-Click to collect them. - -To actually use whatever you loot from those chests open your inventory with 'B'. - -Double click the items in your bag to use or equip them. - -Throw them away by clicking them once and clicking outside of the bag - - -Nights can get pretty dark in Veloren. - -Light your lantern by pressing 'G' - - -Want to free your cursor to close this window? Press TAB! - - -Enjoy your stay in the World of Veloren."#, - -"hud.temp_quest_headline": r#"Please, help us Traveller!"#, -"hud.temp_quest_text": r#"Dungeons filled with evil cultists -have emerged all around our peaceful towns! - - -Gather some company, stack up on food -and defeat their vile leaders and acolytes. - - -Maybe you can even obtain one of their -magically infused items?"#, - - - - // Inventory - "hud.bag.inventory": "{playername}'s Inventory", - "hud.bag.stats_title": "{playername}'s Stats", - "hud.bag.exp": "Exp", - "hud.bag.armor": "Armor", - "hud.bag.stats": "Stats", - "hud.bag.head": "Head", - "hud.bag.neck": "Neck", - "hud.bag.tabard": "Tabard", - "hud.bag.shoulders": "Shoulders", - "hud.bag.chest": "Chest", - "hud.bag.hands": "Hands", - "hud.bag.lantern": "Lantern", - "hud.bag.glider": "Glider", - "hud.bag.belt": "Belt", - "hud.bag.ring": "Ring", - "hud.bag.back": "Back", - "hud.bag.legs": "Legs", - "hud.bag.feet": "Feet", - "hud.bag.mainhand": "Mainhand", - "hud.bag.offhand": "Offhand", - - - // Map and Questlog - "hud.map.map_title": "Map", - "hud.map.qlog_title": "Quests", - - // Settings - "hud.settings.general": "General", - "hud.settings.none": "None", - "hud.settings.press_behavior.toggle": "Toggle", - "hud.settings.press_behavior.hold": "Hold", - "hud.settings.help_window": "Help Window", - "hud.settings.debug_info": "Debug Info", - "hud.settings.tips_on_startup": "Tips-On-Startup", - "hud.settings.ui_scale": "UI-Scale", - "hud.settings.relative_scaling": "Relative Scaling", - "hud.settings.custom_scaling": "Custom Scaling", - "hud.settings.crosshair": "Crosshair", - "hud.settings.transparency": "Transparency", - "hud.settings.hotbar": "Hotbar", - "hud.settings.toggle_shortcuts": "Toggle Shortcuts", - "hud.settings.buffs_skillbar": "Buffs at Skillbar", - "hud.settings.buffs_mmap": "Buffs at Minimap", - "hud.settings.toggle_bar_experience": "Toggle Experience Bar", - "hud.settings.scrolling_combat_text": "Scrolling Combat Text", - "hud.settings.single_damage_number": "Single Damage Numbers", - "hud.settings.cumulated_damage": "Cumulated Damage", - "hud.settings.incoming_damage": "Incoming Damage", - "hud.settings.cumulated_incoming_damage": "Cumulated Incoming Damage", - "hud.settings.speech_bubble": "Speech Bubble", - "hud.settings.speech_bubble_dark_mode": "Speech Bubble Dark Mode", - "hud.settings.speech_bubble_icon": "Speech Bubble Icon", - "hud.settings.energybar_numbers": "Energybar Numbers", - "hud.settings.values": "Values", - "hud.settings.percentages": "Percentages", - "hud.settings.chat": "Chat", - "hud.settings.background_transparency": "Background Transparency", - "hud.settings.chat_character_name": "Character Names in chat", - "hud.settings.loading_tips": "Loading Screen Tips", - - "hud.settings.pan_sensitivity": "Pan Sensitivity", - "hud.settings.zoom_sensitivity": "Zoom Sensitivity", - "hud.settings.invert_scroll_zoom": "Invert Scroll Zoom", - "hud.settings.invert_mouse_y_axis": "Invert Mouse Y Axis", - "hud.settings.enable_mouse_smoothing": "Camera Smoothing", - "hud.settings.free_look_behavior": "Free look behavior", - "hud.settings.auto_walk_behavior": "Auto walk behavior", - "hud.settings.stop_auto_walk_on_input": "Stop auto walk on movement", - - "hud.settings.view_distance": "View Distance", - "hud.settings.sprites_view_distance": "Sprites View Distance", - "hud.settings.figures_view_distance": "Entities View Distance", - "hud.settings.maximum_fps": "Maximum FPS", - "hud.settings.fov": "Field of View (deg)", - "hud.settings.gamma": "Gamma", - "hud.settings.exposure": "Exposure", - "hud.settings.ambiance": "Ambiance Brightness", - "hud.settings.antialiasing_mode": "AntiAliasing Mode", - "hud.settings.upscale_factor": "Upscale Factor", - "hud.settings.cloud_rendering_mode": "Cloud Rendering Mode", - "hud.settings.fluid_rendering_mode": "Fluid Rendering Mode", - "hud.settings.fluid_rendering_mode.cheap": "Cheap", - "hud.settings.fluid_rendering_mode.shiny": "Shiny", - "hud.settings.cloud_rendering_mode.minimal": "Minimal", - "hud.settings.cloud_rendering_mode.low": "Low", - "hud.settings.cloud_rendering_mode.medium": "Medium", - "hud.settings.cloud_rendering_mode.high": "High", - "hud.settings.cloud_rendering_mode.ultra": "Ultra", - "hud.settings.fullscreen": "Fullscreen", - "hud.settings.fullscreen_mode": "Fullscreen Mode", - "hud.settings.fullscreen_mode.exclusive": "Exclusive", - "hud.settings.fullscreen_mode.borderless": "Borderless", - "hud.settings.particles": "Particles", - "hud.settings.resolution": "Resolution", - "hud.settings.bit_depth": "Bit Depth", - "hud.settings.refresh_rate": "Refresh Rate", - "hud.settings.lighting_rendering_mode": "Lighting Rendering Mode", - "hud.settings.lighting_rendering_mode.ashikhmin": "Type A - High ", - "hud.settings.lighting_rendering_mode.blinnphong": "Type B - Medium", - "hud.settings.lighting_rendering_mode.lambertian": "Type L - Cheap", - "hud.settings.shadow_rendering_mode": "Shadow Rendering Mode", - "hud.settings.shadow_rendering_mode.none": "None", - "hud.settings.shadow_rendering_mode.cheap": "Cheap", - "hud.settings.shadow_rendering_mode.map": "Map", - "hud.settings.shadow_rendering_mode.map.resolution": "Resolution", - "hud.settings.lod_detail": "LoD Detail", - "hud.settings.save_window_size": "Save window size", - - - "hud.settings.music_volume": "Music Volume", - "hud.settings.sound_effect_volume": "Sound Effects Volume", - "hud.settings.audio_device": "Audio Device", - - "hud.settings.awaitingkey": "Press a key...", - "hud.settings.unbound": "None", - "hud.settings.reset_keybinds": "Reset to Defaults", - - "hud.social": "Other Players", - "hud.social.online": "Online:", - "hud.social.friends": "Friends", - "hud.social.not_yet_available": "Not yet available", - "hud.social.faction": "Faction", - "hud.social.play_online_fmt": "{nb_player} player(s) online", - "hud.social.name": "Name", - "hud.social.level": "Level", - "hud.social.zone": "Zone", - "hud.social.account": "Account", - - - "hud.crafting": "Crafting", - "hud.crafting.recipes": "Recipes", - "hud.crafting.ingredients": "Ingredients:", - "hud.crafting.craft": "Craft", - "hud.crafting.tool_cata": "Requires:", - - "hud.group": "Group", - "hud.group.invite_to_join": "{name} invited you to their group!", - "hud.group.invite": "Invite", - "hud.group.kick": "Kick", - "hud.group.assign_leader": "Assign Leader", - "hud.group.leave": "Leave Group", - "hud.group.dead" : "Dead", - "hud.group.out_of_range": "Out of range", - "hud.group.add_friend": "Add to Friends", - "hud.group.link_group": "Link Groups", - "hud.group.in_menu": "In Menu", - "hud.group.members": "Group Members", - - "hud.spell": "Spells", - - "hud.free_look_indicator": "Free look active. Press {key} to disable.", - "hud.auto_walk_indicator": "Auto walk active", - - "hud.map.difficulty": "Difficulty", - "hud.map.towns": "Towns", - "hud.map.castles": "Castles", - "hud.map.dungeons": "Dungeons", - "hud.map.caves": "Caves", - "hud.map.cave": "Cave", - "hud.map.town": "Town", - "hud.map.castle": "Castle", - "hud.map.dungeon": "Dungeon", - "hud.map.difficulty_dungeon": "Dungeon\n\nDifficulty: {difficulty}", - "hud.map.drag": "Drag", - "hud.map.zoom": "Zoom", - "hud.map.recenter": "Recenter", - - /// End HUD section - - - /// Start GameInput section - - "gameinput.primary": "Basic Attack", - "gameinput.secondary": "Secondary Attack/Block/Aim", - "gameinput.slot1": "Hotbar Slot 1", - "gameinput.slot2": "Hotbar Slot 2", - "gameinput.slot3": "Hotbar Slot 3", - "gameinput.slot4": "Hotbar Slot 4", - "gameinput.slot5": "Hotbar Slot 5", - "gameinput.slot6": "Hotbar Slot 6", - "gameinput.slot7": "Hotbar Slot 7", - "gameinput.slot8": "Hotbar Slot 8", - "gameinput.slot9": "Hotbar Slot 9", - "gameinput.slot10": "Hotbar Slot 10", - "gameinput.swaploadout": "Swap Loadout", - "gameinput.togglecursor": "Toggle Cursor", - "gameinput.help": "Toggle Help Window", - "gameinput.toggleinterface": "Toggle Interface", - "gameinput.toggledebug": "Toggle FPS and Debug Info", - "gameinput.screenshot": "Take Screenshot", - "gameinput.toggleingameui": "Toggle Nametags", - "gameinput.fullscreen": "Toggle Fullscreen", - "gameinput.moveforward": "Move Forward", - "gameinput.moveleft": "Move Left", - "gameinput.moveright": "Move Right", - "gameinput.moveback": "Move Backwards", - "gameinput.jump": "Jump", - "gameinput.glide": "Glider", - "gameinput.roll": "Roll", - "gameinput.climb": "Climb", - "gameinput.climbdown": "Climb Down", - "gameinput.wallleap": "Wall Leap", - "gameinput.togglelantern": "Toggle Lantern", - "gameinput.mount": "Mount", - "gameinput.chat": "Chat", - "gameinput.command": "Command", - "gameinput.escape": "Escape", - "gameinput.map": "Map", - "gameinput.bag": "Bag", - "gameinput.social": "Social", - "gameinput.sit": "Sit", - "gameinput.spellbook": "Spells", - "gameinput.settings": "Settings", - "gameinput.respawn": "Respawn", - "gameinput.charge": "Charge", - "gameinput.togglewield": "Toggle Wield", - "gameinput.interact": "Interact", - "gameinput.freelook": "Free Look", - "gameinput.autowalk": "Auto Walk", - "gameinput.dance": "Dance", - "gameinput.select": "Select Entity", - "gameinput.acceptgroupinvite": "Accept Group Invite", - "gameinput.declinegroupinvite": "Decline Group Invite", - "gameinput.crafting": "Crafting", - "gameinput.fly": "Fly", - "gameinput.sneak": "Sneak", - "gameinput.swimdown": "Swim downwards", - "gameinput.swimup": "Swim upwards", - - /// End GameInput section - - - /// Start chracter selection section - "char_selection.loading_characters": "Loading characters...", - "char_selection.delete_permanently": "Permanently delete this Character?", - "char_selection.deleting_character": "Deleting Character...", - "char_selection.change_server": "Change Server", - "char_selection.enter_world": "Enter World", - "char_selection.logout": "Logout", - "char_selection.create_new_character": "Create New Character", - "char_selection.creating_character": "Creating Character...", - "char_selection.character_creation": "Character Creation", - - "char_selection.human_default": "Human Default", - "char_selection.level_fmt": "Level {level_nb}", - "char_selection.uncanny_valley": "Wilderness", - "char_selection.plains_of_uncertainty": "Plains of Uncertainty", - "char_selection.beard": "Beard", - "char_selection.hair_style": "Hair Style", - "char_selection.hair_color": "Hair Color", - "char_selection.eye_color": "Eye Color", - "char_selection.skin": "Skin", - "char_selection.eyeshape": "Eye Details", - "char_selection.accessories": "Accessories", - "char_selection.create_info_name": "Your Character needs a name!", - - /// End character selection section - - - /// Start character window section - "character_window.character_name": "Character Name", - // Character stats - "character_window.character_stats": r#"Endurance - -Fitness - -Willpower - -Protection -"#, - /// End character window section - - - /// Start Escape Menu Section - "esc_menu.logout": "Logout", - "esc_menu.quit_game": "Quit Game", - /// End Escape Menu Section - - /// Buffs and Debuffs - "buff.remove": "Click to remove", - "buff.title.missing": "Missing Title", - "buff.desc.missing": "Missing Description", - // Buffs - "buff.title.heal": "Heal", - "buff.desc.heal": "Gain health over time.", - "buff.title.potion": "Potion", - "buff.desc.potion": "Drinking...", - "buff.title.saturation": "Saturation", - "buff.desc.saturation": "Gain health over time from consumables.", - "buff.title.campfire_heal": "Campfire Heal", - "buff.desc.campfire_heal": "Resting at a campfire heals 1% per second.", - // Debuffs - "debuff.title.bleed": "Bleeding", - "debuff.desc.bleed": "Inflicts regular damage.", - }, - - - vector_map: { - "loading.tips": [ - "Press 'G' to light your lantern.", - "Press 'F1' to see all default keybindings.", - "You can type /say or /s to only chat with players directly around you.", - "You can type /region or /r to only chat with players a couple of hundred blocks around you.", - "You can type /group or /g to only chat with players in your current group.", - "To send private messages type /tell followed by a player name and your message.", - "NPCs with the same level can have a different difficulty.", - "Keep an eye out for food, chests and other loot spread all around the world!", - "Inventory filled with food? Try crafting better food from it!", - "Wondering what's there to do? Try out one of the dungeons marked on the map!", - "Don't forget to adjust the graphics for your system. Press 'N' to open the settings.", - "Playing with others is fun! Press 'O' to see who is online.", - "An NPC with a skull beneath their healthbar is quite powerful compared to yourself.", - "Press 'J' to dance. Party!", - "Press 'L-Shift' to open your Glider and conquer the skies.", - "Veloren is still in Pre-Alpha. We do our best to improve it every day!", - "If you want to join the Dev-Team or just have a chat with us join our Discord-Server.", - "You can toggle showing your amount of health on the healthbar in the settings.", - "In order to see your stats click the 'Stats' button in the inventory.", - "Sit near a campfire (with the 'K' key) to rest - receiving a slow heal-over-time.", - ], - "npc.speech.villager_under_attack": [ - "Help, I'm under attack!", - "Help! I'm under attack!", - "Ouch! I'm under attack!", - "Ouch! I'm under attack! Help!", - "Help me! I'm under attack!", - "I'm under attack! Help!", - "I'm under attack! Help me!", - "Help!", - "Help! Help!", - "Help! Help! Help!", - "I'm under attack!", - "AAAHHH! I'm under attack!", - "AAAHHH! I'm under attack! Help!", - "Help! We're under attack!", - "Help! Murderer!", - "Help! There's a murderer on the loose!", - "Help! They're trying to kill me!", - "Guards, I'm under attack!", - "Guards! I'm under attack!", - "I'm under attack! Guards!", - "Help! Guards! I'm under attack!", - "Guards! Come quick!", - "Guards! Guards!", - "Guards! There's a villain attacking me!", - "Guards, slay this foul villain!", - "Guards! There's a murderer!", - "Guards! Help me!", - "You won't get away with this! Guards!", - "You fiend!", - "Help me!", - "Help! Please!", - "Ouch! Guards! Help!", - "They're coming for me!", - "Help! Help! I'm being repressed!", - "Ah, now we see the violence inherent in the system.", - "Tis but a scratch!", - "Stop that!", - "What did I ever do to you?!", - "Please stop attacking me!", - "Hey! Watch where you point that thing!", - "Heinous wretch, be gone with you!", - "Stop it! Go away!", - "Now you're making me mad!", - "Oi! Who do you think you are?!", - "I'll have your head for that!", - "Stop, please! I carry nothing of value!", - "I'll set my brother on you, he's bigger than I am!", - "Nooo, I'm telling mother!", - "Curse you!", - "Please don't do that.", - "That wasn't very nice!", - "Your weapon works, you can put it away now!", - "Spare me!", - "Please, I have a family!", - "I'm too young to die!", - "Can we talk about this?", - "Violence is never the answer!", - "Today is turning out to be a very bad day...", - "Hey, that hurt!", - "Eek!", - "How rude!", - "Stop, I beg you!", - "A pox upon you!", - "This isn't fun.", - "How dare you?!", - "You'll pay for that!", - "Keep that up and you'll be sorry!", - "Don't make me hurt you!", - "There must be some misunderstanding!", - "You don't need to do this!", - "Be gone, fiend!", - "That really hurt!", - "Why would you do that?", - "By the spirits, cease!", - "You must have me confused with someone else!", - "I don't deserve this!", - "Please don't do that again.", - "Guards, throw this monster in the lake!", - "I'll set my tarrasque on you!", - ], - } -) diff --git a/assets/voxygen/i18n/en/_manifest.ron b/assets/voxygen/i18n/en/_manifest.ron new file mode 100644 index 0000000000..f365afa14e --- /dev/null +++ b/assets/voxygen/i18n/en/_manifest.ron @@ -0,0 +1,145 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + metadata: ( + language_name: "English", + language_identifier: "en", + ), + convert_utf8_to_ascii: false, + fonts: { + "opensans": Font ( + asset_key: "voxygen.font.OpenSans-Regular", + scale_ratio: 1.0, + ), + "metamorph": Font ( + asset_key: "voxygen.font.Metamorphous-Regular", + scale_ratio: 1.0, + ), + "alkhemi": Font ( + asset_key: "voxygen.font.Alkhemikal", + scale_ratio: 1.0, + ), + "wizard": Font ( + asset_key: "voxygen.font.wizard", + scale_ratio: 1.0, + ), + "cyri": Font ( + asset_key: "voxygen.font.haxrcorp_4089_cyrillic_altgr_extended", + scale_ratio: 1.0, + ), + }, + sub_directories: [ + "hud" + ], + + string_map: { + + }, + + vector_map: { + "loading.tips": [ + "Press 'G' to light your lantern.", + "Press 'F1' to see all default keybindings.", + "You can type /say or /s to only chat with players directly around you.", + "You can type /region or /r to only chat with players a couple of hundred blocks around you.", + "You can type /group or /g to only chat with players in your current group.", + "To send private messages type /tell followed by a player name and your message.", + "NPCs with the same level can have a different difficulty.", + "Keep an eye out for food, chests and other loot spread all around the world!", + "Inventory filled with food? Try crafting better food from it!", + "Wondering what's there to do? Try out one of the dungeons marked on the map!", + "Don't forget to adjust the graphics for your system. Press 'N' to open the settings.", + "Playing with others is fun! Press 'O' to see who is online.", + "An NPC with a skull beneath their healthbar is quite powerful compared to yourself.", + "Press 'J' to dance. Party!", + "Press 'L-Shift' to open your Glider and conquer the skies.", + "Veloren is still in Pre-Alpha. We do our best to improve it every day!", + "If you want to join the Dev-Team or just have a chat with us join our Discord-Server.", + "You can toggle showing your amount of health on the healthbar in the settings.", + "In order to see your stats click the 'Stats' button in the inventory.", + "Sit near a campfire (with the 'K' key) to rest - receiving a slow heal-over-time.", + ], + "npc.speech.villager_under_attack": [ + "Help, I'm under attack!", + "Help! I'm under attack!", + "Ouch! I'm under attack!", + "Ouch! I'm under attack! Help!", + "Help me! I'm under attack!", + "I'm under attack! Help!", + "I'm under attack! Help me!", + "Help!", + "Help! Help!", + "Help! Help! Help!", + "I'm under attack!", + "AAAHHH! I'm under attack!", + "AAAHHH! I'm under attack! Help!", + "Help! We're under attack!", + "Help! Murderer!", + "Help! There's a murderer on the loose!", + "Help! They're trying to kill me!", + "Guards, I'm under attack!", + "Guards! I'm under attack!", + "I'm under attack! Guards!", + "Help! Guards! I'm under attack!", + "Guards! Come quick!", + "Guards! Guards!", + "Guards! There's a villain attacking me!", + "Guards, slay this foul villain!", + "Guards! There's a murderer!", + "Guards! Help me!", + "You won't get away with this! Guards!", + "You fiend!", + "Help me!", + "Help! Please!", + "Ouch! Guards! Help!", + "They're coming for me!", + "Help! Help! I'm being repressed!", + "Ah, now we see the violence inherent in the system.", + "Tis but a scratch!", + "Stop that!", + "What did I ever do to you?!", + "Please stop attacking me!", + "Hey! Watch where you point that thing!", + "Heinous wretch, be gone with you!", + "Stop it! Go away!", + "Now you're making me mad!", + "Oi! Who do you think you are?!", + "I'll have your head for that!", + "Stop, please! I carry nothing of value!", + "I'll set my brother on you, he's bigger than I am!", + "Nooo, I'm telling mother!", + "Curse you!", + "Please don't do that.", + "That wasn't very nice!", + "Your weapon works, you can put it away now!", + "Spare me!", + "Please, I have a family!", + "I'm too young to die!", + "Can we talk about this?", + "Violence is never the answer!", + "Today is turning out to be a very bad day...", + "Hey, that hurt!", + "Eek!", + "How rude!", + "Stop, I beg you!", + "A pox upon you!", + "This isn't fun.", + "How dare you?!", + "You'll pay for that!", + "Keep that up and you'll be sorry!", + "Don't make me hurt you!", + "There must be some misunderstanding!", + "You don't need to do this!", + "Be gone, fiend!", + "That really hurt!", + "Why would you do that?", + "By the spirits, cease!", + "You must have me confused with someone else!", + "I don't deserve this!", + "Please don't do that again.", + "Guards, throw this monster in the lake!", + "I'll set my tarrasque on you!", + ], + } +) diff --git a/assets/voxygen/i18n/en/buff.ron b/assets/voxygen/i18n/en/buff.ron new file mode 100644 index 0000000000..b1a8b8a401 --- /dev/null +++ b/assets/voxygen/i18n/en/buff.ron @@ -0,0 +1,22 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + string_map: { + "buff.remove": "Click to remove", + "buff.title.missing": "Missing Title", + "buff.desc.missing": "Missing Description", + "buff.title.heal": "Heal", + "buff.desc.heal": "Gain health over time.", + "buff.title.potion": "Potion", + "buff.desc.potion": "Drinking...", + "buff.title.saturation": "Saturation", + "buff.desc.saturation": "Gain health over time from consumables.", + "buff.title.campfire_heal": "Campfire Heal", + "buff.desc.campfire_heal": "Resting at a campfire heals 1% per second.", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/en/char_selection.ron b/assets/voxygen/i18n/en/char_selection.ron new file mode 100644 index 0000000000..a078c83a81 --- /dev/null +++ b/assets/voxygen/i18n/en/char_selection.ron @@ -0,0 +1,31 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + string_map: { + "char_selection.loading_characters": "Loading characters...", + "char_selection.delete_permanently": "Permanently delete this Character?", + "char_selection.deleting_character": "Deleting Character...", + "char_selection.change_server": "Change Server", + "char_selection.enter_world": "Enter World", + "char_selection.logout": "Logout", + "char_selection.create_new_character": "Create New Character", + "char_selection.creating_character": "Creating Character...", + "char_selection.character_creation": "Character Creation", + "char_selection.human_default": "Human Default", + "char_selection.level_fmt": "Level {level_nb}", + "char_selection.uncanny_valley": "Wilderness", + "char_selection.plains_of_uncertainty": "Plains of Uncertainty", + "char_selection.beard": "Beard", + "char_selection.hair_style": "Hair Style", + "char_selection.hair_color": "Hair Color", + "char_selection.eye_color": "Eye Color", + "char_selection.skin": "Skin", + "char_selection.eyeshape": "Eye Details", + "char_selection.accessories": "Accessories", + "char_selection.create_info_name": "Your Character needs a name!", + }, + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/en/common.ron b/assets/voxygen/i18n/en/common.ron new file mode 100644 index 0000000000..987228462f --- /dev/null +++ b/assets/voxygen/i18n/en/common.ron @@ -0,0 +1,72 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + string_map: { + // Texts used in multiple locations with the same formatting + "common.username": "username", + "common.singleplayer": "Singleplayer", + "common.multiplayer": "Multiplayer", + "common.servers": "Servers", + "common.quit": "Quit", + "common.settings": "Settings", + "common.languages": "Languages", + "common.interface": "Interface", + "common.gameplay": "Gameplay", + "common.controls": "Controls", + "common.video": "Graphics", + "common.sound": "Sound", + "common.resume": "Resume", + "common.characters": "Characters", + "common.close": "Close", + "common.yes": "Yes", + "common.no": "No", + "common.back": "Back", + "common.create": "Create", + "common.okay": "Okay", + "common.add": "Add", + "common.accept": "Accept", + "common.decline": "Decline", + "common.disclaimer": "Disclaimer", + "common.cancel": "Cancel", + "common.none": "None", + "common.error": "Error", + "common.fatal_error": "Fatal Error", + "common.you": "You", + "common.automatic": "Auto", + "common.random": "Random", + + // Settings Window title + "common.interface_settings": "Interface Settings", + "common.gameplay_settings": "Gameplay Settings", + "common.controls_settings": "Controls Settings", + "common.video_settings": "Graphics Settings", + "common.sound_settings": "Sound Settings", + "common.language_settings": "Language Settings", + + // Message when connection to the server is lost + "common.connection_lost": r#"Connection lost! +Did the server restart? +Is the client up to date?"#, + + + "common.species.orc": "Orc", + "common.species.human": "Human", + "common.species.dwarf": "Dwarf", + "common.species.elf": "Elf", + "common.species.undead": "Undead", + "common.species.danari": "Danari", + + "common.weapons.axe": "Axe", + "common.weapons.sword": "Sword", + "common.weapons.staff": "Staff", + "common.weapons.bow": "Bow", + "common.weapons.hammer": "Hammer", + "common.weapons.sceptre": "Healing Sceptre", + "common.rand_appearance": "Random appearance and name", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/en/debuff.ron b/assets/voxygen/i18n/en/debuff.ron new file mode 100644 index 0000000000..df35b4df6b --- /dev/null +++ b/assets/voxygen/i18n/en/debuff.ron @@ -0,0 +1,13 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + string_map: { + "debuff.title.bleed": "Bleeding", + "debuff.desc.bleed": "Inflicts regular damage.", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/en/esc_menu.ron b/assets/voxygen/i18n/en/esc_menu.ron new file mode 100644 index 0000000000..538453260a --- /dev/null +++ b/assets/voxygen/i18n/en/esc_menu.ron @@ -0,0 +1,13 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + string_map: { + "esc_menu.logout": "Logout", + "esc_menu.quit_game": "Quit Game", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/en/gameinput.ron b/assets/voxygen/i18n/en/gameinput.ron new file mode 100644 index 0000000000..92c7adb51d --- /dev/null +++ b/assets/voxygen/i18n/en/gameinput.ron @@ -0,0 +1,67 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + string_map: { + "gameinput.primary": "Basic Attack", + "gameinput.secondary": "Secondary Attack/Block/Aim", + "gameinput.slot1": "Hotbar Slot 1", + "gameinput.slot2": "Hotbar Slot 2", + "gameinput.slot3": "Hotbar Slot 3", + "gameinput.slot4": "Hotbar Slot 4", + "gameinput.slot5": "Hotbar Slot 5", + "gameinput.slot6": "Hotbar Slot 6", + "gameinput.slot7": "Hotbar Slot 7", + "gameinput.slot8": "Hotbar Slot 8", + "gameinput.slot9": "Hotbar Slot 9", + "gameinput.slot10": "Hotbar Slot 10", + "gameinput.swaploadout": "Swap Loadout", + "gameinput.togglecursor": "Toggle Cursor", + "gameinput.help": "Toggle Help Window", + "gameinput.toggleinterface": "Toggle Interface", + "gameinput.toggledebug": "Toggle FPS and Debug Info", + "gameinput.screenshot": "Take Screenshot", + "gameinput.toggleingameui": "Toggle Nametags", + "gameinput.fullscreen": "Toggle Fullscreen", + "gameinput.moveforward": "Move Forward", + "gameinput.moveleft": "Move Left", + "gameinput.moveright": "Move Right", + "gameinput.moveback": "Move Backwards", + "gameinput.jump": "Jump", + "gameinput.glide": "Glider", + "gameinput.roll": "Roll", + "gameinput.climb": "Climb", + "gameinput.climbdown": "Climb Down", + "gameinput.wallleap": "Wall Leap", + "gameinput.togglelantern": "Toggle Lantern", + "gameinput.mount": "Mount", + "gameinput.chat": "Chat", + "gameinput.command": "Command", + "gameinput.escape": "Escape", + "gameinput.map": "Map", + "gameinput.bag": "Bag", + "gameinput.social": "Social", + "gameinput.sit": "Sit", + "gameinput.spellbook": "Spells", + "gameinput.settings": "Settings", + "gameinput.respawn": "Respawn", + "gameinput.charge": "Charge", + "gameinput.togglewield": "Toggle Wield", + "gameinput.interact": "Interact", + "gameinput.freelook": "Free Look", + "gameinput.autowalk": "Auto Walk", + "gameinput.dance": "Dance", + "gameinput.select": "Select Entity", + "gameinput.acceptgroupinvite": "Accept Group Invite", + "gameinput.declinegroupinvite": "Decline Group Invite", + "gameinput.crafting": "Crafting", + "gameinput.fly": "Fly", + "gameinput.sneak": "Sneak", + "gameinput.swimdown": "Swim downwards", + "gameinput.swimup": "Swim upwards", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/en/hud/bag.ron b/assets/voxygen/i18n/en/hud/bag.ron new file mode 100644 index 0000000000..e19e086eb5 --- /dev/null +++ b/assets/voxygen/i18n/en/hud/bag.ron @@ -0,0 +1,32 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + string_map: { + // Inventory + "hud.bag.inventory": "{playername}'s Inventory", + "hud.bag.stats_title": "{playername}'s Stats", + "hud.bag.exp": "Exp", + "hud.bag.armor": "Armor", + "hud.bag.stats": "Stats", + "hud.bag.head": "Head", + "hud.bag.neck": "Neck", + "hud.bag.tabard": "Tabard", + "hud.bag.shoulders": "Shoulders", + "hud.bag.chest": "Chest", + "hud.bag.hands": "Hands", + "hud.bag.lantern": "Lantern", + "hud.bag.glider": "Glider", + "hud.bag.belt": "Belt", + "hud.bag.ring": "Ring", + "hud.bag.back": "Back", + "hud.bag.legs": "Legs", + "hud.bag.feet": "Feet", + "hud.bag.mainhand": "Mainhand", + "hud.bag.offhand": "Offhand", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/en/hud/char_window.ron b/assets/voxygen/i18n/en/hud/char_window.ron new file mode 100644 index 0000000000..e8de38bd64 --- /dev/null +++ b/assets/voxygen/i18n/en/hud/char_window.ron @@ -0,0 +1,21 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + string_map: { + "character_window.character_name": "Character Name", + // Character stats + "character_window.character_stats": r#"Endurance + +Fitness + +Willpower + +Protection +"#, + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/en/hud/chat.ron b/assets/voxygen/i18n/en/hud/chat.ron new file mode 100644 index 0000000000..1785615df2 --- /dev/null +++ b/assets/voxygen/i18n/en/hud/chat.ron @@ -0,0 +1,37 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + string_map: { + // Chat outputs + "hud.chat.online_msg": "[{name}] is online now", + "hud.chat.offline_msg": "{name} went offline", + + "hud.chat.default_death_msg": "[{name}] died", + "hud.chat.environmental_kill_msg": "[{name}] died in {environment}", + "hud.chat.fall_kill_msg": "[{name}] died from fall damage", + "hud.chat.suicide_msg": "[{name}] died from self-inflicted wounds", + + "hud.chat.pvp_melee_kill_msg": "[{attacker}] defeated [{victim}]", + "hud.chat.pvp_ranged_kill_msg": "[{attacker}] shot [{victim}]", + "hud.chat.pvp_explosion_kill_msg": "[{attacker}] blew up [{victim}]", + "hud.chat.pvp_energy_kill_msg": "[{attacker}] killed [{victim}] with magic", + "hud.chat.pvp_buff_kill_msg": "[{attacker}] killed [{victim}]", + + + "hud.chat.npc_melee_kill_msg": "{attacker} killed [{victim}]", + "hud.chat.npc_ranged_kill_msg": "{attacker} shot [{victim}]", + "hud.chat.npc_explosion_kill_msg": "{attacker} blew up [{victim}]", + "hud.chat.npc_energy_kill_msg": "[{attacker}] killed [{victim}] with magic", + "hud.chat.npc_other_kill_msg": "[{attacker}] killed [{victim}]", + + "hud.chat.loot_msg": "You picked up [{item}]", + "hud.chat.loot_fail": "Your Inventory is full!", + "hud.chat.goodbye": "Goodbye!", + "hud.chat.connection_lost": "Connection lost. Kicking in {time} seconds.", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/en/hud/crafting.ron b/assets/voxygen/i18n/en/hud/crafting.ron new file mode 100644 index 0000000000..5dd30c3fd3 --- /dev/null +++ b/assets/voxygen/i18n/en/hud/crafting.ron @@ -0,0 +1,16 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + string_map: { + "hud.crafting": "Crafting", + "hud.crafting.recipes": "Recipes", + "hud.crafting.ingredients": "Ingredients:", + "hud.crafting.craft": "Craft", + "hud.crafting.tool_cata": "Requires:", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/en/hud/group.ron b/assets/voxygen/i18n/en/hud/group.ron new file mode 100644 index 0000000000..9e726cccce --- /dev/null +++ b/assets/voxygen/i18n/en/hud/group.ron @@ -0,0 +1,23 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + string_map: { + "hud.group": "Group", + "hud.group.invite_to_join": "{name} invited you to their group!", + "hud.group.invite": "Invite", + "hud.group.kick": "Kick", + "hud.group.assign_leader": "Assign Leader", + "hud.group.leave": "Leave Group", + "hud.group.dead" : "Dead", + "hud.group.out_of_range": "Out of range", + "hud.group.add_friend": "Add to Friends", + "hud.group.link_group": "Link Groups", + "hud.group.in_menu": "In Menu", + "hud.group.members": "Group Members", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/en/hud/hud_settings.ron b/assets/voxygen/i18n/en/hud/hud_settings.ron new file mode 100644 index 0000000000..3d19528961 --- /dev/null +++ b/assets/voxygen/i18n/en/hud/hud_settings.ron @@ -0,0 +1,101 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + string_map: { + // Settings + "hud.settings.general": "General", + "hud.settings.none": "None", + "hud.settings.press_behavior.toggle": "Toggle", + "hud.settings.press_behavior.hold": "Hold", + "hud.settings.help_window": "Help Window", + "hud.settings.debug_info": "Debug Info", + "hud.settings.tips_on_startup": "Tips-On-Startup", + "hud.settings.ui_scale": "UI-Scale", + "hud.settings.relative_scaling": "Relative Scaling", + "hud.settings.custom_scaling": "Custom Scaling", + "hud.settings.crosshair": "Crosshair", + "hud.settings.transparency": "Transparency", + "hud.settings.hotbar": "Hotbar", + "hud.settings.toggle_shortcuts": "Toggle Shortcuts", + "hud.settings.buffs_skillbar": "Buffs at Skillbar", + "hud.settings.buffs_mmap": "Buffs at Minimap", + "hud.settings.toggle_bar_experience": "Toggle Experience Bar", + "hud.settings.scrolling_combat_text": "Scrolling Combat Text", + "hud.settings.single_damage_number": "Single Damage Numbers", + "hud.settings.cumulated_damage": "Cumulated Damage", + "hud.settings.incoming_damage": "Incoming Damage", + "hud.settings.cumulated_incoming_damage": "Cumulated Incoming Damage", + "hud.settings.speech_bubble": "Speech Bubble", + "hud.settings.speech_bubble_dark_mode": "Speech Bubble Dark Mode", + "hud.settings.speech_bubble_icon": "Speech Bubble Icon", + "hud.settings.energybar_numbers": "Energybar Numbers", + "hud.settings.values": "Values", + "hud.settings.percentages": "Percentages", + "hud.settings.chat": "Chat", + "hud.settings.background_transparency": "Background Transparency", + "hud.settings.chat_character_name": "Character Names in chat", + "hud.settings.loading_tips": "Loading Screen Tips", + + "hud.settings.pan_sensitivity": "Pan Sensitivity", + "hud.settings.zoom_sensitivity": "Zoom Sensitivity", + "hud.settings.invert_scroll_zoom": "Invert Scroll Zoom", + "hud.settings.invert_mouse_y_axis": "Invert Mouse Y Axis", + "hud.settings.enable_mouse_smoothing": "Camera Smoothing", + "hud.settings.free_look_behavior": "Free look behavior", + "hud.settings.auto_walk_behavior": "Auto walk behavior", + "hud.settings.stop_auto_walk_on_input": "Stop auto walk on movement", + + "hud.settings.view_distance": "View Distance", + "hud.settings.sprites_view_distance": "Sprites View Distance", + "hud.settings.figures_view_distance": "Entities View Distance", + "hud.settings.maximum_fps": "Maximum FPS", + "hud.settings.fov": "Field of View (deg)", + "hud.settings.gamma": "Gamma", + "hud.settings.exposure": "Exposure", + "hud.settings.ambiance": "Ambiance Brightness", + "hud.settings.antialiasing_mode": "AntiAliasing Mode", + "hud.settings.upscale_factor": "Upscale Factor", + "hud.settings.cloud_rendering_mode": "Cloud Rendering Mode", + "hud.settings.fluid_rendering_mode": "Fluid Rendering Mode", + "hud.settings.fluid_rendering_mode.cheap": "Cheap", + "hud.settings.fluid_rendering_mode.shiny": "Shiny", + "hud.settings.cloud_rendering_mode.minimal": "Minimal", + "hud.settings.cloud_rendering_mode.low": "Low", + "hud.settings.cloud_rendering_mode.medium": "Medium", + "hud.settings.cloud_rendering_mode.high": "High", + "hud.settings.cloud_rendering_mode.ultra": "Ultra", + "hud.settings.fullscreen": "Fullscreen", + "hud.settings.fullscreen_mode": "Fullscreen Mode", + "hud.settings.fullscreen_mode.exclusive": "Exclusive", + "hud.settings.fullscreen_mode.borderless": "Borderless", + "hud.settings.particles": "Particles", + "hud.settings.resolution": "Resolution", + "hud.settings.bit_depth": "Bit Depth", + "hud.settings.refresh_rate": "Refresh Rate", + "hud.settings.lighting_rendering_mode": "Lighting Rendering Mode", + "hud.settings.lighting_rendering_mode.ashikhmin": "Type A - High ", + "hud.settings.lighting_rendering_mode.blinnphong": "Type B - Medium", + "hud.settings.lighting_rendering_mode.lambertian": "Type L - Cheap", + "hud.settings.shadow_rendering_mode": "Shadow Rendering Mode", + "hud.settings.shadow_rendering_mode.none": "None", + "hud.settings.shadow_rendering_mode.cheap": "Cheap", + "hud.settings.shadow_rendering_mode.map": "Map", + "hud.settings.shadow_rendering_mode.map.resolution": "Resolution", + "hud.settings.lod_detail": "LoD Detail", + "hud.settings.save_window_size": "Save window size", + + + "hud.settings.music_volume": "Music Volume", + "hud.settings.sound_effect_volume": "Sound Effects Volume", + "hud.settings.audio_device": "Audio Device", + + "hud.settings.awaitingkey": "Press a key...", + "hud.settings.unbound": "None", + "hud.settings.reset_keybinds": "Reset to Defaults", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/en/hud/map.ron b/assets/voxygen/i18n/en/hud/map.ron new file mode 100644 index 0000000000..ee0db0fe71 --- /dev/null +++ b/assets/voxygen/i18n/en/hud/map.ron @@ -0,0 +1,27 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + string_map: { + // Map and Questlog + "hud.map.map_title": "Map", + "hud.map.qlog_title": "Quests", + "hud.map.difficulty": "Difficulty", + "hud.map.towns": "Towns", + "hud.map.castles": "Castles", + "hud.map.dungeons": "Dungeons", + "hud.map.caves": "Caves", + "hud.map.cave": "Cave", + "hud.map.town": "Town", + "hud.map.castle": "Castle", + "hud.map.dungeon": "Dungeon", + "hud.map.difficulty_dungeon": "Dungeon\n\nDifficulty: {difficulty}", + "hud.map.drag": "Drag", + "hud.map.zoom": "Zoom", + "hud.map.recenter": "Recenter", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/en/hud/misc.ron b/assets/voxygen/i18n/en/hud/misc.ron new file mode 100644 index 0000000000..bdc675c119 --- /dev/null +++ b/assets/voxygen/i18n/en/hud/misc.ron @@ -0,0 +1,75 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + string_map: { + "hud.do_not_show_on_startup": "Don't show this on Startup", + "hud.show_tips": "Show Tips", + "hud.quests": "Quests", + "hud.you_died": "You Died", + "hud.waypoint_saved": "Waypoint Saved", + + "hud.press_key_to_show_keybindings_fmt": "[{key}] Keybindings", + "hud.press_key_to_toggle_lantern_fmt": "[{key}] Lantern", + "hud.press_key_to_show_debug_info_fmt": "Press {key} to show debug info", + "hud.press_key_to_toggle_keybindings_fmt": "Press {key} to toggle keybindings", + "hud.press_key_to_toggle_debug_info_fmt": "Press {key} to toggle debug info", + + // Respawn message + "hud.press_key_to_respawn": r#"Press {key} to respawn at the last campfire you visited."#, + + // Welcome message + "hud.welcome": r#"Welcome to the Veloren Alpha! + + +Some tips before you start: + + +Press F1 to see the available key commands. + +Type /help into the chat to see chat commands + + +There are chests and other objects randomly spawning in the World! + +Right-Click to collect them. + +To actually use whatever you loot from those chests open your inventory with 'B'. + +Double click the items in your bag to use or equip them. + +Throw them away by clicking them once and clicking outside of the bag + + +Nights can get pretty dark in Veloren. + +Light your lantern by pressing 'G' + + +Want to free your cursor to close this window? Press TAB! + + +Enjoy your stay in the World of Veloren."#, + +"hud.temp_quest_headline": r#"Please, help us Traveller!"#, +"hud.temp_quest_text": r#"Dungeons filled with evil cultists +have emerged all around our peaceful towns! + + +Gather some company, stack up on food +and defeat their vile leaders and acolytes. + + +Maybe you can even obtain one of their +magically infused items?"#, + + "hud.spell": "Spells", + + "hud.free_look_indicator": "Free look active. Press {key} to disable.", + "hud.auto_walk_indicator": "Auto walk active", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/en/hud/sct.ron b/assets/voxygen/i18n/en/hud/sct.ron new file mode 100644 index 0000000000..a3a3cfca33 --- /dev/null +++ b/assets/voxygen/i18n/en/hud/sct.ron @@ -0,0 +1,14 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + string_map: { + // SCT outputs + "hud.sct.experience": "{amount} Exp", + "hud.sct.block": "BLOCKED", + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/en/hud/social.ron b/assets/voxygen/i18n/en/hud/social.ron new file mode 100644 index 0000000000..576f0bee35 --- /dev/null +++ b/assets/voxygen/i18n/en/hud/social.ron @@ -0,0 +1,22 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + string_map: { + "hud.social": "Other Players", + "hud.social.online": "Online:", + "hud.social.friends": "Friends", + "hud.social.not_yet_available": "Not yet available", + "hud.social.faction": "Faction", + "hud.social.play_online_fmt": "{nb_player} player(s) online", + "hud.social.name": "Name", + "hud.social.level": "Level", + "hud.social.zone": "Zone", + "hud.social.account": "Account", + }, + + + vector_map: { + } +) + diff --git a/assets/voxygen/i18n/en/main.ron b/assets/voxygen/i18n/en/main.ron new file mode 100644 index 0000000000..4f0c73ef12 --- /dev/null +++ b/assets/voxygen/i18n/en/main.ron @@ -0,0 +1,66 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + string_map: { + /// Start Main screen section + "main.username": "Username", + "main.server": "Server", + "main.password": "Password", + "main.connecting": "Connecting", + "main.creating_world": "Creating world", + "main.tip": "Tip:", + + // Welcome notice that appears the first time Veloren is started + "main.notice": r#"Welcome to the alpha version of Veloren! + +Before you dive into the fun, please keep a few things in mind: + +- This is a very early alpha. Expect bugs, extremely unfinished gameplay, unpolished mechanics, and missing features. + +- If you have constructive feedback or bug reports, you can contact us via Reddit, GitLab, or our community Discord server. + +- Veloren is licensed under the GPL 3 open-source licence. That means you're free to play, modify, and redistribute the game however + you wish (provided derived work is also under GPL 3). + +- Veloren is a non-profit community project, and everybody working on it is a volunteer. +If you like what you see, you're welcome to join the development or art teams! + +Thanks for taking the time to read this notice, we hope you enjoy the game! + +~ The Veloren Devs"#, + + // Login process description + "main.login_process": r#"Information on the Login Process: + +Please note that you now need an account +to play on auth-enabled servers. + +You can create an account over at + +https://veloren.net/account/."#, + "main.login.server_not_found": "Server not found", + "main.login.authentication_error": "Auth error on server", + "main.login.server_full": "Server is full", + "main.login.untrusted_auth_server": "Auth server not trusted", + "main.login.outdated_client_or_server": "ServerWentMad: Probably versions are incompatible, check for updates.", + "main.login.timeout": "Timeout: Server did not respond in time. (Overloaded or network issues).", + "main.login.server_shut_down": "Server shut down", + "main.login.already_logged_in": "You are already logged into the server.", + "main.login.network_error": "Network error", + "main.login.failed_sending_request": "Request to Auth server failed", + "main.login.invalid_character": "The selected character is invalid", + "main.login.client_crashed": "Client crashed", + "main.login.not_on_whitelist": "You need a Whitelist entry by an Admin to join", + "main.login.banned": "You have been banned with the following reason", + "main.login.kicked": "You have been kicked with the following reason", + "main.login.select_language": "Select a language", + + "main.servers.select_server": "Select a server", + /// End Main screen section + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/en/template.ron b/assets/voxygen/i18n/en/template.ron new file mode 100644 index 0000000000..f0d8b704da --- /dev/null +++ b/assets/voxygen/i18n/en/template.ron @@ -0,0 +1,12 @@ +/// WARNING: Localization files shall be saved in UTF-8 format without BOM + +/// Localization for "global" English +( + string_map: { + + }, + + + vector_map: { + } +) diff --git a/assets/voxygen/i18n/es_ES.ron b/assets/voxygen/i18n/es_ES/_manifest.ron similarity index 99% rename from assets/voxygen/i18n/es_ES.ron rename to assets/voxygen/i18n/es_ES/_manifest.ron index a78e33a4b9..3c3b3905d6 100644 --- a/assets/voxygen/i18n/es_ES.ron +++ b/assets/voxygen/i18n/es_ES/_manifest.ron @@ -40,6 +40,7 @@ scale_ratio: 1.0, ), }, + sub_directories: [], string_map: { /// Start Common section // Texts used in multiple locations with the same formatting diff --git a/assets/voxygen/i18n/es_la.ron b/assets/voxygen/i18n/es_la/_manifest.ron similarity index 99% rename from assets/voxygen/i18n/es_la.ron rename to assets/voxygen/i18n/es_la/_manifest.ron index 43d8297186..26eb3c3078 100644 --- a/assets/voxygen/i18n/es_la.ron +++ b/assets/voxygen/i18n/es_la/_manifest.ron @@ -41,6 +41,7 @@ scale_ratio: 1.0, ), }, + sub_directories: [], string_map: { /// Start Common section // Texts used in multiple locations with the same formatting diff --git a/assets/voxygen/i18n/fr_FR.ron b/assets/voxygen/i18n/fr_FR/_manifest.ron similarity index 99% rename from assets/voxygen/i18n/fr_FR.ron rename to assets/voxygen/i18n/fr_FR/_manifest.ron index e2830950d4..8fe307b634 100644 --- a/assets/voxygen/i18n/fr_FR.ron +++ b/assets/voxygen/i18n/fr_FR/_manifest.ron @@ -27,6 +27,7 @@ scale_ratio: 0.9, ), }, + sub_directories: [], string_map: { // Common texts used in multiple locations "common.username": "pseudo", diff --git a/assets/voxygen/i18n/it_IT.ron b/assets/voxygen/i18n/it_IT/_manifest.ron similarity index 99% rename from assets/voxygen/i18n/it_IT.ron rename to assets/voxygen/i18n/it_IT/_manifest.ron index 5a37d39d36..1d06e11ded 100644 --- a/assets/voxygen/i18n/it_IT.ron +++ b/assets/voxygen/i18n/it_IT/_manifest.ron @@ -42,6 +42,8 @@ scale_ratio: 1.0, ), }, + + sub_directories: [], string_map: { /// Start Common section // Texts used in multiple locations with the same formatting diff --git a/assets/voxygen/i18n/nl.ron b/assets/voxygen/i18n/nl/_manifest.ron similarity index 99% rename from assets/voxygen/i18n/nl.ron rename to assets/voxygen/i18n/nl/_manifest.ron index ed2c33f119..c8b0611d08 100644 --- a/assets/voxygen/i18n/nl.ron +++ b/assets/voxygen/i18n/nl/_manifest.ron @@ -41,6 +41,7 @@ scale_ratio: 1.0, ), }, + sub_directories: [], string_map: { /// Start Common section // Texts used in multiple locations with the same formatting diff --git a/assets/voxygen/i18n/no.ron b/assets/voxygen/i18n/no/_manifest.ron similarity index 99% rename from assets/voxygen/i18n/no.ron rename to assets/voxygen/i18n/no/_manifest.ron index de01c4a6d6..a2c7c1f520 100644 --- a/assets/voxygen/i18n/no.ron +++ b/assets/voxygen/i18n/no/_manifest.ron @@ -41,6 +41,7 @@ scale_ratio: 1.0, ), }, + sub_directories: [], string_map: { /// Start Common section // Texts used in multiple locations with the same formatting diff --git a/assets/voxygen/i18n/pt_BR.ron b/assets/voxygen/i18n/pt_BR/_manifest.ron similarity index 99% rename from assets/voxygen/i18n/pt_BR.ron rename to assets/voxygen/i18n/pt_BR/_manifest.ron index f0d4d64eb1..dd791304cc 100644 --- a/assets/voxygen/i18n/pt_BR.ron +++ b/assets/voxygen/i18n/pt_BR/_manifest.ron @@ -27,6 +27,7 @@ scale_ratio: 1.0, ), }, + sub_directories: [], string_map: { /// Start Common section // Texts used in multiple locations with the same formatting diff --git a/assets/voxygen/i18n/pt_PT.ron b/assets/voxygen/i18n/pt_PT/_manifest.ron similarity index 99% rename from assets/voxygen/i18n/pt_PT.ron rename to assets/voxygen/i18n/pt_PT/_manifest.ron index 87bd22bcc7..7aa830fd9b 100644 --- a/assets/voxygen/i18n/pt_PT.ron +++ b/assets/voxygen/i18n/pt_PT/_manifest.ron @@ -27,6 +27,7 @@ scale_ratio: 1.0, ), }, + sub_directories: [], string_map: { /// Start Common section // Texts used in multiple locations with the same formatting diff --git a/assets/voxygen/i18n/ru_RU.ron b/assets/voxygen/i18n/ru_RU/_manifest.ron similarity index 99% rename from assets/voxygen/i18n/ru_RU.ron rename to assets/voxygen/i18n/ru_RU/_manifest.ron index a663e12cf9..1931b3bdc6 100644 --- a/assets/voxygen/i18n/ru_RU.ron +++ b/assets/voxygen/i18n/ru_RU/_manifest.ron @@ -41,6 +41,7 @@ scale_ratio: 1.0, ), }, + sub_directories: [], string_map: { /// Start Common section // Texts used in multiple locations with the same formatting diff --git a/assets/voxygen/i18n/sv.ron b/assets/voxygen/i18n/sv/_manifest.ron similarity index 99% rename from assets/voxygen/i18n/sv.ron rename to assets/voxygen/i18n/sv/_manifest.ron index 45071db229..f7ab658926 100644 --- a/assets/voxygen/i18n/sv.ron +++ b/assets/voxygen/i18n/sv/_manifest.ron @@ -40,6 +40,7 @@ scale_ratio: 1.0, ), }, + sub_directories: [], string_map: { /// Start Common section diff --git a/assets/voxygen/i18n/tr_TR.ron b/assets/voxygen/i18n/tr_TR/_manifest.ron similarity index 99% rename from assets/voxygen/i18n/tr_TR.ron rename to assets/voxygen/i18n/tr_TR/_manifest.ron index 522b9c2ab1..1ede09e42c 100644 --- a/assets/voxygen/i18n/tr_TR.ron +++ b/assets/voxygen/i18n/tr_TR/_manifest.ron @@ -41,6 +41,7 @@ scale_ratio: 1.0, ), }, + sub_directories: [], string_map: { /// Start Common section // Texts used in multiple locations with the same formatting diff --git a/assets/voxygen/i18n/uk_UA.ron b/assets/voxygen/i18n/uk_UA/_manifest.ron similarity index 99% rename from assets/voxygen/i18n/uk_UA.ron rename to assets/voxygen/i18n/uk_UA/_manifest.ron index d5106cbef7..8bcadaf3ce 100644 --- a/assets/voxygen/i18n/uk_UA.ron +++ b/assets/voxygen/i18n/uk_UA/_manifest.ron @@ -41,6 +41,7 @@ scale_ratio: 1.0, ), }, + sub_directories: [], string_map: { /// Start Common section // Texts used in multiple locations with the same formatting diff --git a/assets/voxygen/i18n/zh_CN.ron b/assets/voxygen/i18n/zh_CN/_manifest.ron similarity index 99% rename from assets/voxygen/i18n/zh_CN.ron rename to assets/voxygen/i18n/zh_CN/_manifest.ron index 9273541188..b0ae3c75f4 100644 --- a/assets/voxygen/i18n/zh_CN.ron +++ b/assets/voxygen/i18n/zh_CN/_manifest.ron @@ -41,6 +41,7 @@ scale_ratio: 0.75, ), }, + sub_directories: [], string_map: { /// Start Common section // Texts used in multiple locations with the same formatting diff --git a/assets/voxygen/i18n/zh_TW.ron b/assets/voxygen/i18n/zh_TW/_manifest.ron similarity index 99% rename from assets/voxygen/i18n/zh_TW.ron rename to assets/voxygen/i18n/zh_TW/_manifest.ron index 493e6bf423..45b8d3d95a 100644 --- a/assets/voxygen/i18n/zh_TW.ron +++ b/assets/voxygen/i18n/zh_TW/_manifest.ron @@ -27,6 +27,7 @@ scale_ratio: 0.75, ), }, + sub_directories: [], string_map: { /// Start Common section // Texts used in multiple locations with the same formatting diff --git a/voxygen/src/i18n.rs b/voxygen/src/i18n.rs index d55f81637c..47756afe3e 100644 --- a/voxygen/src/i18n.rs +++ b/voxygen/src/i18n.rs @@ -2,13 +2,14 @@ use common::assets::{self, AssetExt}; use deunicode::deunicode; use hashbrown::{HashMap, HashSet}; use serde::{Deserialize, Serialize}; -use std::borrow::Cow; use tracing::warn; /// The reference language, aka the more up-to-date localization data. /// Also the default language at first startup. pub const REFERENCE_LANG: &str = "en"; +pub const LANG_MANIFEST_FILE: &str = "_manifest"; + /// How a language can be described #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct LanguageMetadata { @@ -42,9 +43,25 @@ impl Font { /// Store font metadata pub type Fonts = HashMap; +/// Raw localization data, expect the strings to not be loaded here +/// However, metadata informations are correct +/// See `Localization` for more info on each attributes +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +pub struct RawLocalization { + pub sub_directories: Vec, + pub string_map: HashMap, + pub vector_map: HashMap>, + pub convert_utf8_to_ascii: bool, + pub fonts: Fonts, + pub metadata: LanguageMetadata, +} + /// Store internationalization data #[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct Localization { + /// A list of subdirectories to lookup for localization files + pub sub_directories: Vec, + /// A map storing the localized texts /// /// Localized content can be accessed using a String key. @@ -65,6 +82,21 @@ pub struct Localization { pub metadata: LanguageMetadata, } +/// Store internationalization maps +/// These structs are meant to be merged into a Localization +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub struct LocalizationFragment { + /// A map storing the localized texts + /// + /// Localized content can be accessed using a String key. + pub string_map: HashMap, + + /// A map for storing variations of localized texts, for example multiple + /// ways of saying "Help, I'm under attack". Used primarily for npc + /// dialogue. + pub vector_map: HashMap>, +} + impl Localization { /// Get a localized text from the given key /// @@ -131,42 +163,107 @@ impl Localization { } } } +impl From for Localization { + fn from(raw: RawLocalization) -> Self { + Self { + sub_directories: raw.sub_directories, + string_map: raw.string_map, + vector_map: raw.vector_map, + convert_utf8_to_ascii: raw.convert_utf8_to_ascii, + fonts: raw.fonts, + metadata: raw.metadata, + } + } +} +impl From for LocalizationFragment { + fn from(raw: RawLocalization) -> Self { + Self { + string_map: raw.string_map, + vector_map: raw.vector_map, + } + } +} -impl assets::Asset for Localization { - type Loader = LocalizationLoader; +impl assets::Asset for RawLocalization { + type Loader = assets::RonLoader; + + const EXTENSION: &'static str = "ron"; +} +impl assets::Asset for LocalizationFragment { + type Loader = assets::RonLoader; const EXTENSION: &'static str = "ron"; } -pub struct LocalizationLoader; -impl assets::Loader for LocalizationLoader { - fn load(content: Cow<[u8]>, ext: &str) -> Result { - let mut asked_localization: Localization = assets::RonLoader::load(content, ext)?; +impl assets::Compound for Localization { + fn load( + cache: &assets::AssetCache, + asset_key: &str, + ) -> Result { + let raw = cache + .load::(&(asset_key.to_string() + "." + LANG_MANIFEST_FILE))? + .cloned(); + let mut localization = Localization::from(raw); + + // Walk through files in the folder, collecting localization fragment to merge + // inside the asked_localization + for localization_asset in cache.load_dir::(asset_key)?.iter() { + localization + .string_map + .extend(localization_asset.read().string_map.clone()); + localization + .vector_map + .extend(localization_asset.read().vector_map.clone()); + } + + // Use the localization's subdirectory list to load fragments from there + for sub_directory in localization.sub_directories.iter() { + for localization_asset in cache + .load_dir::(&(asset_key.to_string() + "." + &sub_directory))? + .iter() + { + localization + .string_map + .extend(localization_asset.read().string_map.clone()); + localization + .vector_map + .extend(localization_asset.read().vector_map.clone()); + } + } // Update the text if UTF-8 to ASCII conversion is enabled - if asked_localization.convert_utf8_to_ascii { - for value in asked_localization.string_map.values_mut() { + if localization.convert_utf8_to_ascii { + for value in localization.string_map.values_mut() { *value = deunicode(value); } - for value in asked_localization.vector_map.values_mut() { + for value in localization.vector_map.values_mut() { *value = value.iter().map(|s| deunicode(s)).collect(); } } - asked_localization.metadata.language_name = - deunicode(&asked_localization.metadata.language_name); + localization.metadata.language_name = deunicode(&localization.metadata.language_name); - Ok(asked_localization) + Ok(localization) } } /// Load all the available languages located in the voxygen asset directory pub fn list_localizations() -> Vec { - assets::load_dir::("voxygen.i18n") - .unwrap() - .iter_all() - .filter_map(|(_, lang)| lang.ok().map(|e| e.read().metadata.clone())) - .collect() + let mut languages = vec![]; + // List language directories + for i18n_directory in std::fs::read_dir("assets/voxygen/i18n").unwrap() { + if let Ok(i18n_entry) = i18n_directory { + if let Some(i18n_key) = i18n_entry.file_name().to_str() { + // load the root file of all the subdirectories + if let Ok(localization) = RawLocalization::load( + &("voxygen.i18n.".to_string() + i18n_key + "." + LANG_MANIFEST_FILE), + ) { + languages.push(localization.read().metadata.clone()); + } + } + } + } + languages } /// Return the asset associated with the language_id @@ -174,7 +271,7 @@ pub fn i18n_asset_key(language_id: &str) -> String { "voxygen.i18n.".to_string() #[cfg(test)] mod tests { - use super::Localization; + use super::{LocalizationFragment, RawLocalization, LANG_MANIFEST_FILE, REFERENCE_LANG}; use git2::Repository; use hashbrown::{HashMap, HashSet}; use ron::de::{from_bytes, from_reader}; @@ -183,15 +280,12 @@ mod tests { path::{Path, PathBuf}, }; - /// List localization files as a PathBuf vector - fn i18n_files(i18n_dir: &Path) -> Vec { + /// List localization directories as a PathBuf vector + fn i18n_directories(i18n_dir: &Path) -> Vec { fs::read_dir(i18n_dir) .unwrap() .map(|res| res.map(|e| e.path()).unwrap()) - .filter(|e| match e.extension() { - Some(ext) => ext == "ron", - None => false, - }) + .filter(|e| e.is_dir()) .collect() } @@ -247,7 +341,7 @@ mod tests { fn generate_key_version<'a>( repo: &'a git2::Repository, - localization: &Localization, + localization: &LocalizationFragment, path: &std::path::Path, file_blob: &git2::Blob, ) -> HashMap { @@ -321,42 +415,129 @@ mod tests { keys } + fn complete_key_versions<'a>( + repo: &'a git2::Repository, + head_ref: &git2::Reference, + i18n_key_versions: &mut HashMap, + dir: &Path, + ) { + let root_dir = std::env::current_dir() + .map(|p| p.parent().expect("").to_owned()) + .unwrap(); + for i18n_file in root_dir.join(&dir).read_dir().unwrap() { + if let Ok(i18n_file) = i18n_file { + if let Ok(file_type) = i18n_file.file_type() { + if file_type.is_file() { + let full_path = i18n_file.path(); + let path = full_path.strip_prefix(&root_dir).unwrap(); + println!("-> {:?}", i18n_file.file_name()); + let i18n_blob = read_file_from_path(&repo, &head_ref, &path); + let i18n: LocalizationFragment = match from_bytes(i18n_blob.content()) { + Ok(v) => v, + Err(e) => { + eprintln!( + "Could not parse {} RON file, skipping: {}", + i18n_file.path().to_string_lossy(), + e + ); + continue; + }, + }; + i18n_key_versions + .extend(generate_key_version(&repo, &i18n, &path, &i18n_blob)); + } + } + } + } + } + + fn verify_localization_directory(directory_path: &Path) { + let root_dir = std::env::current_dir() + .map(|p| p.parent().expect("").to_owned()) + .unwrap(); + // Walk through each file in the directory + for i18n_file in root_dir.join(&directory_path).read_dir().unwrap() { + if let Ok(i18n_file) = i18n_file { + if let Ok(file_type) = i18n_file.file_type() { + // Skip folders and the manifest file (which does not contain the same struct we + // want to load) + if file_type.is_file() + && i18n_file.file_name().to_string_lossy() + != (LANG_MANIFEST_FILE.to_string() + ".ron") + { + let full_path = i18n_file.path(); + println!("-> {:?}", full_path.strip_prefix(&root_dir).unwrap()); + let f = fs::File::open(&full_path).expect("Failed opening file"); + let _: LocalizationFragment = match from_reader(f) { + Ok(v) => v, + Err(e) => { + panic!( + "Could not parse {} RON file, error: {}", + full_path.to_string_lossy(), + e + ); + }, + }; + } + } + } + } + } + // Test to verify all languages that they are VALID and loadable, without // need of git just on the local assets folder #[test] fn verify_all_localizations() { // Generate paths let i18n_asset_path = Path::new("assets/voxygen/i18n/"); - let en_i18n_path = i18n_asset_path.join("en.ron"); + let ref_i18n_dir_path = i18n_asset_path.join(REFERENCE_LANG); + let ref_i18n_path = ref_i18n_dir_path.join(LANG_MANIFEST_FILE.to_string() + ".ron"); let root_dir = std::env::current_dir() .map(|p| p.parent().expect("").to_owned()) .unwrap(); assert!( - root_dir.join(&en_i18n_path).is_file(), - "en reference files doesn't exist, something is wrong!" + root_dir.join(&ref_i18n_dir_path).is_dir(), + "Reference language folder doesn't exist, something is wrong!" ); - let i18n_files = i18n_files(&root_dir.join(i18n_asset_path)); + assert!( + root_dir.join(&ref_i18n_path).is_file(), + "Reference language manifest file doesn't exist, something is wrong!" + ); + let i18n_directories = i18n_directories(&root_dir.join(i18n_asset_path)); // This simple check ONLY guarantees that an arbitrary minimum of translation // files exists. It's just to notice unintentional deletion of all // files, or modifying the paths. In case you want to delete all // language you have to adjust this number: assert!( - i18n_files.len() > 5, - "have less than 5 translation files, arbitrary minimum check failed. Maybe the i18n \ + i18n_directories.len() > 5, + "have less than 5 translation folders, arbitrary minimum check failed. Maybe the i18n \ folder is empty?" ); - for path in i18n_files { - let f = fs::File::open(&path).expect("Failed opening file"); - let _: Localization = match from_reader(f) { + for i18n_directory in i18n_directories { + // Attempt to load the manifest file + let manifest_path = i18n_directory.join(LANG_MANIFEST_FILE.to_string() + ".ron"); + println!( + "verifying {:?}", + manifest_path.strip_prefix(&root_dir).unwrap() + ); + let f = fs::File::open(&manifest_path).expect("Failed opening file"); + let raw_localization: RawLocalization = match from_reader(f) { Ok(v) => v, Err(e) => { panic!( "Could not parse {} RON file, error: {}", - path.to_string_lossy(), + i18n_directory.to_string_lossy(), e ); }, }; + // Walk through each files and try to load them + verify_localization_directory(&i18n_directory); + // Walk through each subdirectories and try to load files in them + for sub_directory in raw_localization.sub_directories.iter() { + let subdir_path = &i18n_directory.join(sub_directory); + verify_localization_directory(&subdir_path); + } } } @@ -367,14 +548,21 @@ mod tests { fn test_all_localizations() { // Generate paths let i18n_asset_path = Path::new("assets/voxygen/i18n/"); - let en_i18n_path = i18n_asset_path.join("en.ron"); + let ref_i18n_dir_path = i18n_asset_path.join(REFERENCE_LANG); + let ref_i18n_path = ref_i18n_dir_path.join(LANG_MANIFEST_FILE.to_string() + ".ron"); let root_dir = std::env::current_dir() .map(|p| p.parent().expect("").to_owned()) .unwrap(); let i18n_path = root_dir.join(i18n_asset_path); - if !root_dir.join(&en_i18n_path).is_file() { - panic!("Reference language file not found {:?}", &en_i18n_path) + if !root_dir.join(&ref_i18n_dir_path).is_dir() { + panic!( + "Reference language folder not found {:?}", + &ref_i18n_dir_path + ) + } + if !root_dir.join(&ref_i18n_path).is_file() { + panic!("Reference language file not found {:?}", &ref_i18n_path) } // Initialize Git objects @@ -385,18 +573,31 @@ mod tests { let head_ref = repo.head().expect("Impossible to get the HEAD reference"); // Read HEAD for the reference language file - let i18n_en_blob = read_file_from_path(&repo, &head_ref, &en_i18n_path); - let loc: Localization = from_bytes(i18n_en_blob.content()) + let i18n_ref_blob = read_file_from_path(&repo, &head_ref, &ref_i18n_path); + let loc: RawLocalization = from_bytes(i18n_ref_blob.content()) .expect("Expect to parse reference i18n RON file, can't proceed without it"); - let i18n_references: HashMap = - generate_key_version(&repo, &loc, &en_i18n_path, &i18n_en_blob); + let mut i18n_references: HashMap = generate_key_version( + &repo, + &LocalizationFragment::from(loc.clone()), + &ref_i18n_path, + &i18n_ref_blob, + ); + + // read HEAD for the fragment files + complete_key_versions(&repo, &head_ref, &mut i18n_references, &ref_i18n_dir_path); + // read HEAD for the subfolders + for sub_directory in loc.sub_directories.iter() { + let subdir_path = &ref_i18n_dir_path.join(sub_directory); + complete_key_versions(&repo, &head_ref, &mut i18n_references, &subdir_path); + } // Compare to other reference files - let i18n_files = i18n_files(&i18n_path); + let i18n_directories = i18n_directories(&i18n_path); let mut i18n_entry_counts: HashMap = HashMap::new(); - for file in &i18n_files { - let relfile = file.strip_prefix(&root_dir).unwrap(); - if relfile == en_i18n_path { + for file in &i18n_directories { + let reldir = file.strip_prefix(&root_dir).unwrap(); + let relfile = reldir.join(&(LANG_MANIFEST_FILE.to_string() + ".ron")); + if relfile == ref_i18n_path { continue; } println!("\n-----------------------------------"); @@ -405,7 +606,7 @@ mod tests { // Find the localization entry state let current_blob = read_file_from_path(&repo, &head_ref, &relfile); - let current_loc: Localization = match from_bytes(current_blob.content()) { + let current_loc: RawLocalization = match from_bytes(current_blob.content()) { Ok(v) => v, Err(e) => { eprintln!( @@ -416,8 +617,20 @@ mod tests { continue; }, }; - let mut current_i18n = - generate_key_version(&repo, ¤t_loc, &relfile, ¤t_blob); + let mut current_i18n = generate_key_version( + &repo, + &LocalizationFragment::from(current_loc.clone()), + &relfile, + ¤t_blob, + ); + // read HEAD for the fragment files + complete_key_versions(&repo, &head_ref, &mut current_i18n, &reldir); + // read HEAD for the subfolders + for sub_directory in current_loc.sub_directories.iter() { + let subdir_path = &reldir.join(sub_directory); + complete_key_versions(&repo, &head_ref, &mut current_i18n, &subdir_path); + } + for (ref_key, ref_state) in i18n_references.iter() { match current_i18n.get_mut(ref_key) { Some(state) => { @@ -478,7 +691,7 @@ mod tests { "State", "Key name", relfile.to_str().unwrap(), - en_i18n_path.to_str().unwrap() + ref_i18n_path.to_str().unwrap() ); let mut sorted_keys: Vec<&String> = current_i18n.keys().collect();